<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        canvas{
            background: gray;
        }

    </style>
</head>
<body>
    <!-- 颜色选择器，用于选择绘制矩形的颜色 -->
    <input type="color"/>
    <!-- Canvas画布元素 -->
    <canvas></canvas> 
    <!-- 清除画布按钮 -->
    <button onclick="clearCanvas()">
        清除画布
    </button>
    <script>
        // 获取颜色选择器DOM元素
        const colorPicker = document.querySelector('input');
        // 获取Canvas DOM元素
        const canvas = document.querySelector('canvas');
        // 获取Canvas 2D绘图上下文，用于绘制图形
        const ctx = canvas.getContext('2d');

        /**
         * 初始化Canvas尺寸和显示比例
         * 设置Canvas的实际分辨率和CSS显示尺寸，解决高分辨率屏幕模糊问题
         */
        function init(){
            const w = 500;  // Canvas显示宽度
            const h = 300;  // Canvas显示高度
            // 设置Canvas实际像素尺寸，乘以设备像素比以获得更清晰的显示效果
            canvas.width = w * devicePixelRatio;
            canvas.height = h * devicePixelRatio;
            // 设置Canvas在页面中的显示尺寸
            canvas.style.width = w + 'px';
            canvas.style.height = h + 'px';
            
        }
        init();

        // 存储所有绘制的矩形对象数组
        let shapes = [];

        /**
         * 矩形类，用于创建和管理矩形对象
         */
        class Rectangle{
            /**
             * 构造函数，初始化矩形属性
             * @param {number} startx - 矩形起始点x坐标
             * @param {number} starty - 矩形起始点y坐标
             * @param {string} color - 矩形填充颜色
             */
            constructor(startx,starty,color){
                this.startx = startx;  // 矩形起始点x坐标
                this.starty = starty;  // 矩形起始点y坐标
                this.color = color;    // 矩形填充颜色
                this.endx = startx;    // 矩形结束点x坐标，初始值与起始点相同
                this.endy = starty;    // 矩形结束点y坐标，初始值与起始点相同
            }
            
            // 计算矩形最小x坐标（只读属性）
            get minX(){
                return Math.min(this.startx,this.endx);
            }
            
            // 计算矩形最小y坐标（只读属性）
            get minY(){
                return Math.min(this.starty,this.endy);
            }
            
            // 计算矩形最大x坐标（只读属性）
            get maxX(){
                return Math.max(this.startx,this.endx);
            }
            
            // 计算矩形最大y坐标（只读属性）
            get maxY(){
                return Math.max(this.starty,this.endy);
            }

            /**
             * 绘制矩形方法
             * 根据矩形的坐标和颜色在Canvas上绘制矩形
             */
            draw(){
                ctx.fillStyle = this.color;  // 设置填充颜色
                // 绘制矩形，坐标和尺寸都乘以设备像素比以适配高分辨率屏幕
                ctx.fillRect(
                            this.minX*devicePixelRatio,
                            this.minY*devicePixelRatio,
                           ( this.maxX-this.minX)*devicePixelRatio,
                            (this.maxY-this.minY)*devicePixelRatio);
            }

            /**
             * 改变矩形结束点坐标方法
             * @param {number} newX - 新的结束点x坐标
             * @param {number} newY - 新的结束点y坐标
             */
            change(newX,newY){
                this.endx = newX;
                this.endy = newY;
            } 
            
            /**
             * 判断点是否在矩形内部方法
             * @param {number} x - 待检测点的x坐标
             * @param {number} y - 待检测点的y坐标
             * @returns {boolean} - 如果点在矩形内返回true，否则返回false
             */
            isInside(x,y){
                return (x >= this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY);
            };
            
            /**
             * 移动矩形位置方法
             * 保持矩形大小不变，改变其位置
             * @param {number} newX - 新位置的x坐标
             * @param {number} newY - 新位置的y坐标
             */
            moveChange(newX,newY){
                const width = this.maxX - this.minX;   // 计算矩形宽度
                const height = this.maxY - this.minY;  // 计算矩形高度
                this.startx = newX;                    // 更新起始点坐标
                this.starty = newY;
                this.endx = newX +width;               // 根据新起始点和原尺寸计算结束点坐标
                this.endy = newY + height;
            }
        }

        /**
         * 根据坐标查找包含该点的矩形
         * 从后往前遍历，确保返回最上层的矩形
         * @param {number} x - 待检测点的x坐标
         * @param {number} y - 待检测点的y坐标
         * @returns {Rectangle|null} - 找到的矩形对象或null
         */
        function getShape(x,y){
            for ( let i =shapes.length-1; i >=0; i--){
                const shape = shapes[i];
                if (shape.isInside(x,y)){
                    return shape;
                }
            }
            return null;
        }

        // 获取Canvas元素相对于视口的位置信息
        const rect = canvas.getBoundingClientRect();
        
        /**
         * 鼠标按下事件处理函数
         * 处理绘制新矩形或拖拽已有矩形的逻辑
         */
       canvas.onmousedown = function(e){
        const efx = e.offsetX;   // 鼠标相对于Canvas元素的x坐标
        const efy = e.offsetY;   // 鼠标相对于Canvas元素的y坐标
        const shape = getShape(efx,efy);  // 查找鼠标点击位置的矩形
        
        // 如果点击在已有矩形上，则进行拖拽操作
        if (shape){
            // 计算鼠标点击点相对于矩形起始点的偏移量
            const widthX = efx - shape.startx;
            const heightY = efy - shape.starty;
            
            // 鼠标移动事件处理函数 - 拖拽矩形
            window.onmousemove = (ev)=>{ 
                // 计算鼠标在Canvas中的坐标
                const ex  = ev.clientX-rect.left;
                const ey  = ev.clientY-rect.top;
                // 根据鼠标位置和偏移量更新矩形位置
                shape.moveChange(ex-widthX,ey-heightY);
            };
            
            // 鼠标释放事件处理函数 - 结束拖拽
            window.onmouseup = (eve)=>{ 
                window.onmousemove = null;  // 清除鼠标移动事件处理器
                window.onmouseup = null;    // 清除鼠标释放事件处理器
            };
        }else{
            // 如果点击在空白区域，则创建新矩形
            const shape = new Rectangle(efx,efy,colorPicker.value);
            shapes.push(shape);  // 将新矩形添加到数组中
            
            // 鼠标移动事件处理函数 - 绘制矩形
            window.onmousemove = (ev)=>{ 
                // 计算鼠标在Canvas中的坐标
                const ex  = ev.clientX - rect.left;
                const ey  = ev.clientY - rect.top;
                // 更新矩形的结束点坐标，实现动态绘制效果
                shape.change(ex,ey);
            };
            
            // 鼠标释放事件处理函数 - 结束绘制
            window.onmouseup = (eve)=>{ 
                window.onmousemove = null;  // 清除鼠标移动事件处理器
                window.onmouseup = null;    // 清除鼠标释放事件处理器
            };
        }

       } 
      
       /**
        * 绘制函数
        * 使用requestAnimationFrame循环绘制所有矩形
        */
       function draw(){
         requestAnimationFrame(draw); // requestAnimationFrame 循环执行 
       
           
            ctx.clearRect(0,0,canvas.width,canvas.height);  // 清除Canvas内容
            // 遍历所有矩形并绘制
            for (const shape of shapes){
                shape.draw();
                console.log('绘制成功');
            }
       
       }
       draw();

       /**
        * 清除画布函数
        * 清空所有矩形对象并重新绘制（实际在draw函数中实现清空）
        */
       function clearCanvas(){
        shapes=[]  // 清空矩形数组
        console.log('清除画布成功');
       }

       
    </script>   
</body>
</html>